package 数据结构系列;

import java.util.HashMap;
import java.util.LinkedHashMap;

public class LRU缓存机制_146 {

    /**
     * 创建双链表的基石
     */
    class Node {
        public int key;
        public int val;
        public Node next;
        public Node prev;

        public Node(int key, int val) {
            this.key = key;
            this.val = val;
        }
    }

    /**
     * 双向链表
     */
    class DoubleList {
        //头节点、尾节点
        private Node head;
        private Node tail;
        //链表元素数目
        private int size;

        public DoubleList() {
            //初始化双向链表的数据
            head = new Node(0, 0);
            tail = new Node(0, 0);
            head.next = tail;
            tail.prev = head;
            size = 0;
        }

        /*
        在链表尾部添加节点x，时间复杂度是O(1)
        发现没有，在这里是给尾部插入x，用到的节点就只有tail和x，没有出现head
         */
        public void addLast(Node x) {
            x.prev = tail.prev;
            x.next = tail;
            tail.prev.next = x;
            tail.prev = x;
            size++;
        }

        /*
        删除链表中的节点x
        之和x有关，所以只出现了x
         */
        public void remove(Node x) {
            x.prev.next = x.next;
            x.next.prev = x.prev;
            size--;
        }

        /*
        删除链表中的第一个节点，并且返回该节点
         */
        public Node removeFirst() {
            if (head.next == tail) {
                return null;
            }
            Node first = head.next;//注意！head是表头，并不是第一个节点
            remove(first);
            return first;
        }

        /*
        返回链表的长度
         */
        public int size() {
            return size;
        }


    }

    //哈希表和双向链表配合使用
    private HashMap<Integer, Node> map;
    private DoubleList cache;
    //最大容量
    private int cap;

    public LRU缓存机制_146(int capacity){
        this.cap=capacity;
        map=new HashMap<>();
        cache=new DoubleList();
    }

    /**
     * 将某个key提升为最近使用的
     */
    private void makeRecently(int key){
        Node x=map.get(key);
        cache.remove(x);
        cache.addLast(x);
    }

    /**
     * 添加最近使用的元素
     */
    private void  addRecently(int key,int val){
        Node x = new Node(key, val);
        //链表的尾部就是最近使用的元素
        cache.addLast(x);
        //记住要在map中存放对应的映射，方便快速找到该节点
        map.put(key,x);
    }

    /**
     * 删除某一个key
     */
    private  void deleteKey(int key){
        Node x = map.get(key);
        cache.remove(x);
        //map的映射关系也记得要删除
        map.remove(key);
    }

    /**
     * 删除最近最久未使用元素
     */
    private  void removeLeastRecently(){
        //链表的第一个元素就是最近最久未使用的元素
        Node first = cache.removeFirst();
        //同时删除map中对应的映射
        map.remove(first.key);
    }


    /*以下就是向外暴露的方法 */

    public  int get(int key){
        if (!map.containsKey(key)){
            return  -1;
        }
        makeRecently(key);
        return map.get(key).val;
    }

    public  void put(int key,int val){
        if (map.containsKey(key)){
            //删除旧的数据
            deleteKey(key);
            //添加新插入的数据为最近使用的数据
            addRecently(key, val);
            return;
        }

        if (cache.size()==cap){//达到了最大容量
            //LRU淘汰
            removeLeastRecently();
        }
        addRecently(key, val);

    }




 /*
    使用了LinkedHashMap



 int cap;//最大容量缓存
    LinkedHashMap<Integer, Integer> cache = new LinkedHashMap<>();

    public LRU缓存机制_146(int capacity) {
        this.cap = capacity;
    }

    public int get(int key) {
        if (!cache.containsKey(key)) {
            return -1;
        }
        //key存在就把key提升为最近使用
        makeRecently(key);

        return cache.get(key);
    }

    public void put(int key, int value) {
        if (cache.containsKey(key)) {//key存在是覆盖的之前的key的位置，不用判断容量
            //修改key的值
            cache.put(key, value);
            //然后将key提升为最近使用的
            makeRecently(key);
            return;
        }
        //如果后台队列已经满了，就要排除最近最久没使用的
        if (cache.size() >= this.cap) {
            //链表头部就是最久未使用的key，因为是从链表尾部插入元素的
            int oldestKey = cache.keySet().iterator().next();
            cache.remove(oldestKey);
        }
        //将新的key添加到链表尾部
        cache.put(key, value);


    }

    public void makeRecently(int key) {
        int val = cache.get(key);
        //删除key再重新插入，就在队尾了
        cache.remove(key);
        cache.put(key, val);
    }


  */
}
